package org.myabstract.graph.model;

import java.util.ArrayList;
import java.util.List;

public class Node<E extends INodeType> {

	private E type;

	private IFilter<INodeType, INodeType> childFilter;

	private IFilter<INodeType, INodeType> parentFilter;

	// the list of references which this node is part of
	private List<Reference<INodeType, INodeType>> references = new ArrayList<Reference<INodeType, INodeType>>();

	// private List<Node<INodeType>> referencedElements = new
	// ArrayList<Node<INodeType>>();

	public boolean isTopLevelElement = false;

	public Node(E type) {
		this.type = type;
	}

	public Node(E type, IFilter<INodeType, INodeType> childFilter,
			IFilter<INodeType, INodeType> parentFilter) {
		this.type = type;
		this.childFilter = childFilter;
		this.parentFilter = parentFilter;
	}

	private <T extends INodeType> List<Node<T>> getReferencedElements(
			IFilter<INodeType, INodeType> filter) {

		List<Node<T>> result = new ArrayList<Node<T>>();

		for (int i = 0; i < references.size(); i++) {
			Node<INodeType> node = references.get(i).getNode(
					(Node<INodeType>) this);
			if (filter != null) {
				if ((filter.accept(node, references.get(i),
						(Node<INodeType>) this))) {
					result.add((Node<T>) node);
				}
			}
		}
		return result;
	}

	public Node<INodeType>[] getChildren() {
		List list = this.getReferencedElements(this.childFilter);

		Node<INodeType>[] result = new Node[list.size()];

		for (int i = 0; i < list.size(); i++) {
			result[i] = (Node<INodeType>) list.get(i);
		}
		return result;
	}

	public Node<INodeType> getParent() {
		List list = this.getReferencedElements(this.parentFilter);
		if (list.size() != 1)
			return null;
		else
			return (Node<INodeType>) list.get(0);
	}

	public <T extends INodeType> Reference<INodeType, INodeType> findReference(
			Class<T> type) {

		for (int i = 0; i < references.size(); i++) {
			if (references.get(i).contains(type))
				return references.get(i);
		}
		return null;
	}

	public E getType() {
		return type;
	}

	public boolean addReference(Reference<INodeType, INodeType> ref) {
		if (ref != null) {
			if (ref.contains(type.getClass())) {
				if (!references.contains(ref)) { // no duplicates
					references.add(ref);
					return true;
				}
			}
		}
		return false;
	}

	public boolean deleteReference(Reference<INodeType, INodeType> ref) {
		if (ref != null)
			if (references.contains(ref)) {
				references.remove(ref);
				return true;
			} else
				return false;
		return false;
	}

	// public void addReferencedElement(Node<INodeType> nod) {
	// if (nod != null)
	// this.referencedElements.add(nod);
	// }

	public void setChildrenFilter(IFilter<INodeType, INodeType> filter) {
		if (filter != null)
			this.childFilter = filter;
	}

	public IFilter<INodeType, INodeType> getChildrenFilter() {
		return this.childFilter;
	}

	public void setParentFilter(IFilter<INodeType, INodeType> filter) {
		if (filter != null)
			this.parentFilter = filter;
	}

	public IFilter<INodeType, INodeType> getParentFilter() {
		return this.parentFilter;
	}

}
